package com.example.wkable.exoplayer.meida_codec

import android.media.MediaCodec
import android.media.MediaExtractor
import android.media.MediaFormat
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.Message
import android.util.Log
import android.view.Surface
import java.nio.ByteBuffer

class VideoPlayer(private val surface: Surface) : BasePlayer {

    private val mediaExtractor: MediaExtractor = MediaExtractor()
    private var mediaCodec: MediaCodec? = null
    private var width: Int = 0
    private var height: Int = 0
    private var originalFrameRate: Int = 0
    private var durationInUs: Long = 0
    private var format: MediaFormat? = null
    private var currentFrameRate: Int = 0
    private var workerThread: WorkerThread? = null

    init {
        workerThread = WorkerThread()
        workerThread!!.start()
    }

    override fun setDataSource(dataSource: String) {
        mediaExtractor.setDataSource(dataSource)
        val trackCnt = mediaExtractor.trackCount
        var trackIndex = -1
        for (i in 0 until trackCnt) {
            val format = mediaExtractor.getTrackFormat(i)
            Log.e(TAG, "format:${format.getString(MediaFormat.KEY_MIME)} size:$trackCnt")
            if (format.getString(MediaFormat.KEY_MIME).contains("video/")) {
                trackIndex = i
                break
            }
        }
        if (trackIndex == -1) {
            throw Exception("no video track available in this data source:$dataSource")
        }

        mediaExtractor.selectTrack(trackIndex)

        val trackFormat = mediaExtractor.getTrackFormat(trackIndex)
        this.format = trackFormat
        val mime = trackFormat.getString(MediaFormat.KEY_MIME)
        width = trackFormat.getInteger(MediaFormat.KEY_WIDTH)
        height = trackFormat.getInteger(MediaFormat.KEY_HEIGHT)
        originalFrameRate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE)
        this.currentFrameRate = originalFrameRate
        durationInUs = trackFormat.getLong(MediaFormat.KEY_DURATION)// in microseconds US

        mediaCodec = MediaCodec.createDecoderByType(mime)
        mediaCodec!!.configure(trackFormat, surface, null, 0)
    }

    override fun start() {
        workerThread!!.getHandler().obtainMessage(MSG_START).sendToTarget()
    }


    override fun pause() {
        workerThread!!.getHandler().obtainMessage(MSG_PAUSE).sendToTarget()
    }


    override fun resume() {

    }

    override fun stop() {

    }

    override fun release() {

    }

    override fun setLooping(loop: Boolean) {

    }

    override fun seek(durationInMs: Long) {

    }

    /**
     * 获取视频的格式
     */
    fun getFormat(): MediaFormat? {
        return format
    }


    companion object {
        private const val TAG = "VideoPlayer"
        const val MSG_START = 1
        const val MSG_PAUSE = 2
    }

    inner class WorkerThread : HandlerThread("video_decoder") {
        private var playing = false
        private var loop = true
        private val frameHelper: FrameRateHelper by lazy {
            FrameRateHelper(originalFrameRate)
        }
        private val _handler: Handler by lazy {
            object : Handler(looper) {
                override fun handleMessage(msg: Message?) {
                    super.handleMessage(msg)
                    msg ?: return
                    when (msg.what) {
                        MSG_START -> {
                            handleStart()
                        }
                        MSG_PAUSE -> {
                            playing = false
                        }
                    }
                }
            }
        }

        private fun handleStart() {
            frameHelper.frameRation = 4.0
            playing = true
            val mediaCodec: MediaCodec = mediaCodec!!
            mediaCodec.start()
            val bufferInfo = MediaCodec.BufferInfo()
            var cnt = 0
            var inputDone = false
            var outputDone = false
            while (playing) {
                if (!surface.isValid) {
                    Log.e(TAG, "surface invalid---------------")
                    return
                }
                if (!inputDone) {
                    val inputBufferIndex = mediaCodec.dequeueInputBuffer(0)
                    if (inputBufferIndex >= 0) {
                        val inputBuffer = getInputBuffers(inputBufferIndex)
                        inputBuffer ?: continue
                        val readSampleData = mediaExtractor.readSampleData(inputBuffer, 0)
                        inputBuffer.clear()
                        if (readSampleData < 0) {
                            mediaCodec.queueInputBuffer(
                                inputBufferIndex,
                                0,
                                0,
                                0,
                                MediaCodec.BUFFER_FLAG_END_OF_STREAM
                            )
                            inputDone = true
                            Log.e(TAG, "end of media input--------------")
                        } else {
                            // surface 无效的时候会崩溃
                            try {
                                mediaCodec.queueInputBuffer(
                                    inputBufferIndex,
                                    0,
                                    // TODO 这里栽了个大坑，不能用inputBuffer.limit，而只能用MediaExtractor.readSampleData返回的长度
                                    readSampleData,
                                    mediaExtractor.sampleTime,
                                    0
                                )
                                mediaExtractor.advance()
                            } catch (e: Exception) {
                                Log.e(TAG, "queueInputBuffer error:$e")
                            }

                        }
                    } else {
                        Log.e(TAG, "invalid input buffer index:$inputBufferIndex")
                    }
                }

                if (!outputDone) {
                    val outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10 * 1000)
                    if (outputBufferIndex >= 0) {
                        cnt++
                        Log.e(
                            TAG,
                            "cnt---------->$cnt bufferInfo:${Integer.toHexString(bufferInfo.flags)}"
                        )
                        if (cnt == 2601) {
                            Log.e(TAG, "normal------------->")
                            frameHelper.frameRation = 1.0
                        }
                        frameHelper.keepPace(bufferInfo.presentationTimeUs)
                        // TODO 如何避免中止的时候抛出异常，即Surface销毁的时候，如何保证程序不崩溃？
                        try {
                            mediaCodec.releaseOutputBuffer(outputBufferIndex, true)
                        } catch (e: Exception) {
                            Log.e(TAG, "releaseOutputBuffer error:$e")
                        }


                        if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
                            Log.e(TAG, "END OF STREAM ........")
                            if (loop) {
                                mediaExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                                mediaCodec.flush()
                                inputDone = false
                                Log.e(TAG, "end of output, looping..........")
                            } else {
                                outputDone = true
                                Log.e(TAG, "end of output, finish play...........")
                                break
                            }
                        }
                    } else {
                        Log.e(TAG, "dequeueOutputBuffer result:$outputBufferIndex")
                    }
                }


            }

        }

        private fun getInputBuffers(index: Int): ByteBuffer? {
            return if (Build.VERSION.SDK_INT >= 21) {
                mediaCodec!!.getInputBuffer(index)
            } else {
                mediaCodec!!.inputBuffers[index]
            }
        }

        fun getHandler(): Handler {
            return _handler
        }


    }

    class FrameRateHelper(private var originalFrameRate: Int) {

        private var lastFrameTimeInMs = 0L
        var frameRation: Double = 1.0

        private val currentFrameRate: Double
            get() {
                return frameRation * originalFrameRate
            }
        private val dividerTimeInMs: Double
            get() {
                return 1000 / currentFrameRate
            }


        fun keepPace(presentationUs: Long) {
            // 如果间隔时间不足，需要sleep，如果间隔时间溢出，需要skip
            if (lastFrameTimeInMs == 0L) {
                lastFrameTimeInMs = System.currentTimeMillis()
                return
            }
            val leftTimeInMs = lastFrameTimeInMs + dividerTimeInMs - System.currentTimeMillis()
            if (leftTimeInMs <= 0) {
                lastFrameTimeInMs = System.currentTimeMillis()
                return
            }
            Log.e(TAG, "sleep time in milliseconds:$leftTimeInMs")
            try {
                Thread.sleep((leftTimeInMs).toLong())
            } catch (e: Exception) {
                Log.e(TAG, "Thread interrupted exception:$e")
            }

            lastFrameTimeInMs = System.currentTimeMillis()
        }

    }

}